#include <asm/system.h>

#include <linux/sched.h>
#include <linux/tty.h>
#include <linux/unistd.h>
#include <linux/kernel.h>
#include <linux/string.h>

extern char _end;

static inline __attribute__((always_inline)) _syscall0(int, idle);
static inline __attribute__((always_inline)) _syscall0(int, fork);
static inline __attribute__((always_inline)) _syscall1(int, setup, void *, BIOS);

extern char empty_zero_page[PAGE_SIZE];

extern void init(void);
extern void kmalloc_init();
extern void blk_dev_init();

#define PARAM         empty_zero_page
#define EXT_MEM_K     (*(unsigned short *)(PARAM + 2))
#define DRIVE_INFO    (*(struct drive_info_struct *)(PARAM + 0x80))
#define SCREEN_INFO   (*(struct screen_info *)(PARAM + 0))
#define ORIG_ROOT_DEV (*(unsigned short *)(PARAM + 0x1FC))

extern void time_init(void);

static unsigned long memory_start = 0;
static unsigned long memory_end = 0;

struct drive_info_struct {
    char dummy[32];
} drive_info;
struct screen_info screen_info;

void start_kernel() {
    ROOT_DEV = ORIG_ROOT_DEV;
    drive_info = DRIVE_INFO;
    screen_info = SCREEN_INFO;
    memory_start = (unsigned long)&_end;
    memory_end = ((1 << 20) + (EXT_MEM_K << 10)) & PAGE_MASK;
    memory_end = __va(memory_end);
    memory_start = paging_init(memory_start, memory_end);
    trap_init();
    sched_init();
    kmalloc_init();
    con_init(); // 屏幕参数初始化之后才能打印
    blk_dev_init();
    mem_init(memory_start, memory_end);
    memset(empty_zero_page, 0, PAGE_SIZE);
    time_init();
    sti(); // 开启中断
    move_to_user_mode();
    if (!fork())
        init();
    for (;;)
        idle();
}

void init(void) {
    setup((void *)&drive_info);
    while (1) {}
}
